package com.ddfeiyu.redis.configuration;

import com.alibaba.fastjson.JSON;
import com.ddfeiyu.redis.exception.RedisException;
import com.ddfeiyu.redis.properties.PoolProperties;
import com.ddfeiyu.redis.properties.RedisClusterProperties;
import com.ddfeiyu.redis.properties.RedisProperties;
import com.ddfeiyu.redis.util.RedisQueueUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Configuration
@AutoConfigureOrder(Integer.MAX_VALUE)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfig {

    @Autowired
    private RedisProperties redisProperties;

    @Primary
    @Bean
    public RedisConnectionFactory redisConnectionFactory(){
        log.info("--RedisClusterConfig.createJedisConnectionFactory---begin,redisProperties:[ {} ]", JSON.toJSONString(redisProperties));
        RedisConnectionFactory jedisConnectionFactory = createJedisConnectionFactory();
        log.info("--RedisClusterConfig.createJedisConnectionFactory---end,jedisConnectionFactory:[ {} ]", jedisConnectionFactory.toString());
        return jedisConnectionFactory;
    }

    private RedisConnectionFactory createJedisConnectionFactory(){
        JedisConnectionFactory connectionFactory = null;
        RedisClusterProperties cluster = redisProperties.getCluster();
        if (cluster !=null ){
            List<String> nodes = cluster.getNodes();
            if (!CollectionUtils.isEmpty(nodes)){
                connectionFactory = new JedisConnectionFactory(createRedisConfiguration());
                connectionFactory.setPoolConfig(createPoolConfig());
                connectionFactory.afterPropertiesSet();
                return connectionFactory;
            }
        }
        connectionFactory = new JedisConnectionFactory();
        connectionFactory.setDatabase(redisProperties.getDataBase());
        connectionFactory.setHostName(redisProperties.getHost());
        connectionFactory.setPort(redisProperties.getPort());
        connectionFactory.setPoolConfig(createPoolConfig());
        connectionFactory.afterPropertiesSet();
        return connectionFactory ;
    }


    private RedisClusterConfiguration createRedisConfiguration(){
        log.info("--RedisClusterConfig.createRedisConfiguration---start");
        List<String> nodes = redisProperties.getCluster().getNodes();
        if (CollectionUtils.isEmpty(nodes)){
            throw new RedisException("请配置集群连接地址");
        }
        List<RedisNode> redisNodes = new ArrayList<>();
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        String[] temp ;
        for (String node: nodes  ) {
            temp = node.split(":");
            redisNodes.add(new RedisNode(temp[0],Integer.valueOf(temp[1])));
        }
        redisClusterConfiguration.setClusterNodes(redisNodes);
        return redisClusterConfiguration;
    }

    private JedisPoolConfig createPoolConfig (){
        log.info("--RedisClusterConfig.createPoolConfig---");
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        PoolProperties pool = redisProperties.getJedis().getPool();
        poolConfig.setMaxIdle(pool.getMaxIdle());
        poolConfig.setMaxWaitMillis(pool.getMaxWait());
        poolConfig.setMaxTotal(pool.getMaxActive());
        poolConfig.setMinIdle(pool.getMinIdle());
        return poolConfig ;
    }


    @Primary
    @Bean
    public StringRedisTemplate redisTemplate(RedisConnectionFactory  redisConnectionFactory){
        log.info("--RedisAutoConfig.redisTemplate---start,redisConnectionFactory:[ {} ]", redisConnectionFactory.toString());
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        redisTemplate.setEnableTransactionSupport(Boolean.FALSE);
        log.info("--RedisAutoConfig.redisTemplate---end,redisTemplate:[ {} ]", redisTemplate.toString());
        return redisTemplate ;
    }


    @Bean
    public RedisQueueUtil redisQueueUtil(StringRedisTemplate redisTemplate){
        log.info("--RedisAutoConfig.redisQueueUtil---start,redisTemplate:[ {} ]",redisTemplate.toString());
        return new RedisQueueUtil(redisTemplate);
    }
}
